Skip to content

test(remote): cover the phone projection on a REAL v3 workspace - #1065

Merged
Juliusolsson05 merged 3 commits into
mainfrom
test/phone-projection-v3
Sep 20, 2026
Merged

Juliusolsson05 merged 3 commits into
mainfrom
test/phone-projection-v3

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented Sep 20, 2026

Copy link
Copy Markdown
Owner

Closes #1031 item 4.

What was actually missing — and what was not

Correction to this PR's original claim. I first wrote that nothing exercised v3 and that a regression "would have been silent". That is false, and the review caught it: agentActivity/workspaceProjection.test.ts already has a projectWorkspace — v3 documents (#992) block, and deleting the v3 projects parsing fails two of its tests. I had run that mutation only against src/main/remote/, seen it green, and generalised.

The real gap is narrower and specific to the phone: nothing drove RemoteWorkspaceProjection — the phone's own read model — with a v3 document at all. Every test in that file built a v2 tabs[]/root tile tree by hand, which is not what the app has persisted since #1013. That class is what the phone's entire session list depends on.

The fixture

The real ~/.config/agent-code/workspace.json from 2026-09-20: one window, 3 projects, 13 sessions across five provider kinds.

The review audited every string in it and found nothing to redact. Two things are worth stating rather than leaving implicit:

  • drafts persists raw composer text. This capture contained none, so the file is clean — but clean by timing, not by process. The README now says to drop drafts unconditionally when re-capturing.
  • UUID identifiers are kept verbatim (tldrIdentity, providerSessionId, tmux names) because the tests join on them and they are inert without the transcript files they key into. If a debug bundle or proxy dump were ever published from the same machine they would become a correlation key — the known trade, and the same one the existing v2 owner fixture makes.

cwd placeholders are /fixture/dir-N, not project-N: two of them are different directories inside one project, so the numbering never tracked Title N.

What the tests pin

The identity test originally asserted only kind and cwd — both pass-throughs. It now asserts tldrIdentity, the key the remote server joins every TLDR and Goal frame on: dropping it silently empties both on the phone while the session list still looks correct.

Mutation Result
null out tldrIdentity in the shared projection 2 failed
remove the tldrIdentity equality check in workspaceProjection.ts 1 failed — it was green under all 74 existing remote tests
delete the v3 projects parsing fails here and in agentActivity (as it always did)

Each test also guards its own premise: the fixture really is v3 with no tabs to fall back on, it places sessions in more than one project, and five provider kinds are represented.

The store fake was duplicated verbatim across both projection test files; one definition now lives in workspaceProjection.testSupport.ts.

npx tsc -b clean; src/main/remote/ green (10 files / 74 tests).

🤖 Generated with Claude Code

#1031 item 4.

Every phone-projection test built a v2 document by hand — `tabs[]` with a
`root` tile tree — so nothing in the suite exercised the shape the app has
actually been persisting since #1013 migrated to the unified stage. The
projection DOES handle v3, but a regression in that half would have been
silent, and the phone's entire session list depends on it.

The proof that this was a real blind spot rather than a tidiness exercise:
deleting the v3 project parsing from `agentActivity/workspaceProjection.ts`
leaves the existing phone suite completely GREEN (4 passed) and fails only the
new file. Ignoring the row's own `projectId` — the v3 membership rule — does
the same.

The input is the real `~/.config/agent-code/workspace.json` the app wrote on
2026-09-20: one window, three projects, 13 sessions across five provider
kinds. Sanitized under the same contract as the v2 fixture — structure and
every value unchanged except `cwd`, `title` and `projectTabTitle`, each mapped
one-to-one so equal values stay equal — and verified afterwards to contain no
absolute home path, username or real directory name.

Each test guards against being vacuous: the first asserts the fixture really
is v3 with no `tabs` to fall back on, the membership test asserts the file
actually places sessions in more than one project, and the identity test
asserts four or more provider kinds are represented.

The v2 test file is deliberately left alone rather than parameterised: main
reads whatever is on disk, and a window that has not saved since the upgrade
still holds a v2 document. Both generations must keep working, so both keep
their own evidence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Juliusolsson05 and others added 2 commits September 20, 2026 02:29
Review of #1065. The most important finding is that my own justification was
false, so it is corrected rather than softened.

`agentActivity/workspaceProjection.test.ts` already covers the SHARED decoder
on v3 documents, and deleting the v3 `projects` parsing fails two of its tests.
I ran that mutation only against `src/main/remote/`, saw it green, and
generalised it into "a regression would have been silent". It would not have
been. The real gap is narrower and specific to the phone: nothing drove
`RemoteWorkspaceProjection` with a v3 document at all. The header, the README
and the PR now say that instead.

The identity test asserted only `kind` and `cwd`, both pass-throughs. It now
asserts `tldrIdentity` — the key the remote server joins every TLDR and Goal
frame on, so dropping it silently empties both on the phone while the session
list still looks right. Nulling it now fails 2. The re-projection test changes
a `tldrIdentity` rather than a `title` for the same reason: that equality check
was unpinned, and removing it left all 74 remote tests green. It now fails 1.
The kind count said "four or more" under a comment naming five; it says five.

The sanitization table was wrong in three ways: it listed `projectTabTitle`,
which is v2-only and appears in neither file; it omitted `projects[].title`,
which carries three of the placeholders; and it said nothing about `drafts`,
which persists RAW COMPOSER TEXT. This capture contained none, so the file is
clean — but clean by timing, not by process, and the README now says to drop
`drafts` unconditionally when re-capturing.

`/fixture/project-N` named directories, not projects — two of them are
different directories inside one project, so the numbering never tracked
`Title N`. They are `/fixture/dir-N`.

The store fake was duplicated verbatim across both projection test files; one
definition of "the store notifies only after bytes reach disk" now lives in
`workspaceProjection.testSupport.ts`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The extracted helper declared a mutable PersistedWindow[][], which the v2
file's own readonly saves could not satisfy. Caught by tsc after the previous
commit was already pushed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Juliusolsson05 added a commit that referenced this pull request Sep 20, 2026
…ght real bugs

Two of the three were my own: #1067's first fix re-created the #1021 input
trap, and #1065's stated justification was false. Both are recorded as such
rather than as generic review findings, because the pattern worth remembering
is that I generalised from a mutation run against too narrow a suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Juliusolsson05
Juliusolsson05 merged commit 07ef6d1 into main Sep 20, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the test/phone-projection-v3 branch September 20, 2026 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unified stage follow-ups: phone agent list after restart, goal loops on pooled agents, dictation with an empty lane

1 participant